package com.zhs.xyzsdk.editors.token;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;

import com.zhs.javacc.Token;
import com.zhs.javacc.XYZParser;
import com.zhs.javacc.XYZParserConstants;

public class TokenManager {
	public static final int OTHER = -1;
	private static Map<IDocument, TokenList> TOKEN_MAP = new HashMap<IDocument, TokenList>();

	/**
	 * Get token list of a document
	 */
	public static int getOffset(int line, int column, IDocument doc) {
		int offset = 0;
		try {
			for (int i = 0; i < line - 1; i++) {
				offset += doc.getLineLength(i);
			}
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		offset = offset + column - 1;
		return offset;
	}

	public static TokenList getTokenList(IDocument doc) {
		TokenList tokenList = TOKEN_MAP.get(doc);
		String string = doc.get();
		if (tokenList == null) {
			System.out.println("re lex...");
			List<CommonToken> list = new ArrayList<CommonToken>();
			int pos = 0;
			try {
				XYZParser parser = new XYZParser(new ByteArrayInputStream(doc
						.get().getBytes()));
				Token currentToken = null;
				do {
					currentToken = parser.getNextToken();
					CommonToken ct = new CommonToken(currentToken, getOffset(
							currentToken.beginLine, currentToken.beginColumn,
							doc), getOffset(currentToken.endLine,
							currentToken.endColumn, doc));

					if (currentToken.kind == XYZParserConstants.EOF) {
						if (pos < string.length()) {
							Token t = new Token(OTHER,
									string.substring(pos, string.length()));
							CommonToken ct1 = new CommonToken(t, pos, string
									.length() - 1);
							list.add(ct1);
						}
						list.add(ct);
					} else {
						if (ct.getStartIndex() == pos) {
							list.add(ct);
							pos = ct.getStopIndex() + 1;
						} else {
							Token t = new Token(OTHER,
									string.substring(pos, ct.getStartIndex()));
							CommonToken ct1 = new CommonToken(t, pos, ct
									.getStartIndex() - 1);
							list.add(ct1);
							list.add(ct);
							pos = ct.getStopIndex() + 1;
						}
					}
				} while (!currentToken.image.equals(""));
			} catch (Error e) {
				e.printStackTrace();
				if (pos < string.length()) {
					Token t = new Token(OTHER,
							string.substring(pos, string.length()));
					CommonToken ct1 = new CommonToken(t, pos, string
							.length() - 1);
					list.add(ct1);
				}
			}
			tokenList = new TokenList();
			tokenList.setTokenList(list);
			TOKEN_MAP.put(doc, tokenList);
		}
		return tokenList;
	}

	/**
	 * Clear token list of a document from cache
	 */
	public static void clearTokenList(IDocument doc) {
		TOKEN_MAP.remove(doc);
	}

	/**
	 * Register a document, then TokenManager will track the document change
	 * event and refresh token list when necessary
	 */
	public static void registerDocument(IDocument doc) {
		doc.addPrenotifiedDocumentListener(new IDocumentListener() {
			public void documentAboutToBeChanged(DocumentEvent event) {
			}

			public void documentChanged(DocumentEvent event) {
				TokenManager.clearTokenList(event.fDocument);
			}
		});
	}
}
